package com.bluesky.plum.uimodels.render.html;

import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.ApplicationContext;

import com.bluesky.javawebbrowser.domain.html.tags.Tag;
import com.bluesky.javawebbrowser.domain.html.tags.form.FormElement;
import com.bluesky.javawebbrowser.domain.html.tags.form.input.CheckBox;
import com.bluesky.plum.domain.Person;
import com.bluesky.plum.richdomain.RichDomainFields;
import com.bluesky.plum.uimodels.render.html.components.containers.window.Event;
import com.bluesky.plum.uimodels.render.html.components.containers.window.HMainWindow;
import com.bluesky.plum.uimodels.render.html.components.containers.window.HWindowThread;
import com.bluesky.plum.uimodels.standard.components.containers.form.FormBuilder;
import com.bluesky.plum.uimodels.standard.components.containers.window.Window;

/**
 * it is a font end controller like struts and other MVC tools. It is designed
 * to parse the url like:
 * 
 * /oum/processEvent?_component_id=Btn_002&_event_name=click&field1=123&field2=
 * xyz <br>
 * or RESTful <br>
 * /oum/btn_002.click?field1=123&field2=xyz
 * 
 * process steps: <br>
 * 1. update field values to server side model <br>
 * == all field components changes will not submit(for Non-Ajax), until a event
 * is submitting. <br>
 * 2. trigger the event handler method. the event has no parameters. (or only ==
 * one parameter, which is 'sender', I guess it's useful when click a cell in a
 * table. this idea is borrowed from Delphi)
 * 
 * 
 * 
 * originally, I think the field names and the form name is enough. Since any
 * field name should be unique in a form. but it can not cover the situation of
 * a event outside a form. eg. a hyper link, from a menu. it could mapped to a
 * event to create and show a form. But it do not belong to a form, mostly when
 * it is a GET visit. And even worse, some components do not have names, they
 * are anonymous. so, I have to give up my old solution, introduce a new
 * solution instead. the new solution will not use the 'name' attribute, while
 * use the 'id' attribute. every component will have a unique id when it is
 * born. So problem solved. in a classic FORM post/get scenario, I'd rather to
 * provide a alternative way--use field name and form name. I don't why. coz
 * it's easier to understand by human?s
 * 
 * below including some obsolete ideas<br>
 * any event (GET/POST) will update the server side model. that means the
 * parameters and their values will be used to update the server side model. the
 * event name will be mapped to server side event. for example, if the event
 * name is onButton1CLick, then the onClick event of the component Button1 will
 * be triggered at server side. even no process event code is written, some
 * value will be updated. here is two different ideas, one is whatever the event
 * name is, the parameters will be updated. the other is only selected
 * parameters will be updated.
 * 
 * 
 *============================================ <br>
 * 2010-4-9 this servlet's url format is like:<br>
 * 1. /oum/create?windowClass=LoginWindow<br>
 * 2. /oum/processEvent?....[event params]
 * 
 * @author jack
 * 
 */
public class PMEServlet extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 3360310842067402939L;

	public static String OUM_MainWindow = "_oum_main_window";
	public static String OUM_FORM = "_oum_form";
	public static String OUM_EVENT_NAME = "_event_name";
	public static String OUM_COMPONENT_ID = "_component_id";

	private static Map<String, FormBuilder> formClasses;
	static {
		formClasses = new HashMap<String, FormBuilder>();

		Person jack = Person.getSample();
		RichDomainFields rdo = RichDomainFields.createFromEntity(jack);

		FormBuilder formCreator;
		// formCreator = new CreatePersonForm(FormBuilderFactory
		// .getFormBuilder(FormBuilderType.HTML), rdo);

		// formClasses.put("CreatePersonForm", formCreator);

	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		// parse url if support RESTful URL pattern
		String requestURI = req.getRequestURI();
		if (requestURI.startsWith("/"))
			requestURI = requestURI.substring(1);
		if (requestURI.endsWith("/"))
			requestURI = requestURI.substring(0, requestURI.length() - 1);

		String[] ss = requestURI.split("/");

		// app/module/command, eg. plum/oum/processEvent
		String appName = ss[0];
		String module = ss[1];
		String command = ss[2];

		if (command.equals("processEvent")) {
			HMainWindow mainWindow = (HMainWindow) req.getSession()
					.getAttribute(OUM_MainWindow);
			Window fontWindow = mainWindow.getWindowManager()
					.getVisibleFontWindow();
			Tag htmlTag = (Tag) fontWindow.getNativeComponent();

			// sync from request to server-side model
			synchronizeFields(req, htmlTag);

			String compomentId = req.getParameter(OUM_COMPONENT_ID);
			String eventName = req.getParameter(OUM_EVENT_NAME);

			Event event = new Event(compomentId, eventName, Thread
					.currentThread());

			System.out.println("PMEServlet.doGet() process event: "
					+ compomentId + "." + eventName);

			HWindowThread windowThread = (HWindowThread) fontWindow
					.getEventHandlerThread();
			windowThread.putEvent(event);

			synchronized (event) {
				try {// wait until the window thread notify it
					System.out
							.println("PMEServlet.doGet() is gonna wait the window thread to process event:"
									+ event);

					event.wait();
					System.out.println("PMEServlet.doGet() wait end of event:"
							+ event);

				} catch (InterruptedException e) {

					e.printStackTrace();
				}
			}

			// after waiting, it is time to render the UI.
			System.out.println("PMEServlet.doGet() rendering UI");
			req.getRequestDispatcher("/app.jsp").forward(req, resp);
		} else if (command.equals("create")) {
			// mostly create a new window
			String windowClass = req.getParameter("windowClass");

		} else {
			req.setAttribute("form", "<P>unknown command</P>");
			req.getRequestDispatcher("/app.jsp").forward(req, resp);

		}

		// super.doGet(req, resp);
	}

	private void synchronizeFields(HttpServletRequest req, Tag htmlTag) {
		System.out.println("start synchronize fields.");

		// first, set all single checkbox to unchecked, because http
		// protocal will not post any value if it not checked
		htmlTag.traverse(new Tag.TagProcessor() {
			@Override
			public boolean process(Tag tag) {
				if (tag instanceof CheckBox) {
					CheckBox cb = (CheckBox) tag;
					cb.setChecked(false);
				}
				return false;
			}
		});

		// then copy state from request to model
		Enumeration<String> em = req.getParameterNames();
		while (em.hasMoreElements()) {
			final String pName = em.nextElement();
			final String pValue = req.getParameter(pName);
			System.out.println("process http param :" + pName);
			htmlTag.traverse(new Tag.TagProcessor() {
				@Override
				public boolean process(Tag tag) {
					if (tag instanceof FormElement) {
						FormElement fe = (FormElement) tag;
						if (fe.getName() != null
								&& fe.getName().compareToIgnoreCase(pName) == 0) {
							System.out.println("set component " + pName + "="
									+ pValue);
							fe.setHttpValue(pValue);
						}
					}
					return false;
				}
			});
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		doGet(req, resp);
	}
}
